#include "game.hh"
#include "render.hh"
#include "gl.hh"
#include "../log.hh"
#include <string>

namespace gl
{
	void game::render(context& ctx, bool ui) const
	{
		if (ui) {
			gl::color4f(1, 1, 1, 1);

			// text "score"
			gl::line_width(2);
			gl::push_matrix();
			gl::translatef(-0.8, 0.8, 0);
			gl::scalef(0.03, 0.03, 1);
			gl::render::text("score", 1.4);
			gl::pop_matrix();

			// score
			gl::line_width(3);
			gl::push_matrix();
			gl::translatef(-0.55, 0.81, 0);
			gl::scalef(0.05, 0.05, 1);
			gl::render::text(std::to_string(int(score)), 1.4);
			gl::pop_matrix();

			if (mode != mode_type::crash) {
				// fps
				gl::line_width(1);
				gl::push_matrix();
				gl::translatef(0.55, 0.8, 0);
				gl::scalef(0.03, 0.03, 1);
				gl::render::text(std::to_string(fps), 1.4);
				gl::pop_matrix();

				// hp
				gl::push_matrix();
				gl::translatef(-0.82, 0.68, 0);
				gl::scalef(0.05, 0.05, 1);
				gl::color3f(1, 1, 1);
				gl::render::square(0, 0, 12, 0.5);
				gl::color3f(1, hp/100, hp/100);
				gl::render::square(0.1, 0.1, 0.1 + 11.8*hp/100, 0.4, true);
				gl::pop_matrix();

				// ep
				gl::push_matrix();
				gl::translatef(-0.82, 0.64, 0);
				gl::scalef(0.05, 0.05, 1);
				gl::color3f(1, 1, 1);
				gl::render::square(0, 0, 12, 0.5);
				gl::color3f(1, ep/100, ep/100);
				gl::render::square(0.1, 0.1, 0.1 + 11.8*ep/100, 0.4, true);
				gl::pop_matrix();
			}

			if (mode == mode_type::crash && !crash_time) {
				gl::line_width(5);
				gl::color3f(1, 1, 1);
				gl::push_matrix();
				gl::scalef(0.1, 0.1, 1);
				gl::translatef(-1.4*2, 0, 0);
				gl::render::text("crash", 1.4);
				gl::pop_matrix();

				gl::line_width(3);
				gl::push_matrix();
				gl::scalef(0.05, 0.05, 1);
				gl::translatef(-1.4*4, -4, 0);
				gl::render::text("hit space", 1.4);
				gl::pop_matrix();
			}

			if (pause) {
				gl::line_width(5);
				gl::color3f(1, 1, 1);
				gl::push_matrix();
				gl::scalef(0.1, 0.1, 1);
				gl::translatef(-1.4*2, 0, 0);
				gl::render::text("pause", 1.4);
				gl::pop_matrix();
			}

			if (mode == mode_type::start) {
				gl::line_width(5);
				gl::color3f(1, 1, 1);
				gl::push_matrix();
				gl::scalef(0.1, 0.1, 1);
				gl::translatef(-1.4*4, 0, 0);
				gl::render::text("pilot 3d", 1.4);
				gl::pop_matrix();

				gl::line_width(3);
				gl::color3f(1, 0.6, 0.1);
				gl::push_matrix();
				gl::scalef(0.05, 0.05, 1);
				gl::translatef(-1.4*10, -4, 0);
				gl::render::text("press space to start", 1.4);
				gl::pop_matrix();
			}
		}
		else {
			gl::push_matrix();
			cam(ctx);
			if (mode != mode_type::crash) scene(ctx);
			ps_drive.render();
			gl::pop_matrix();
		}
	}

	void game::update(float step, gl::context& ctx, int fps)
	{
		if (mode == mode_type::start) {
			if (ctx.key(' '))
				mode = mode_type::normal;
			return;
		}

		this->fps = fps;

		auto target = cam.target();
		scene.update(target);
		target.z -= 2;	// use it for particle position and collision

		if (mode == mode_type::crash) {
			if (ctx.key(' ')) {
				reset();
				mode = mode_type::normal;
			}
			if (crash_time > 0) {
				for (int i=0; i<20; i++)
					ps_drive.add(
							300,				// life
							target,				// pos
							{ 0, 0, 0 },		// velocity
							{ 0, 0, 1 },		// acceleration
							{ 0.5, 0.3, 0.1 });	// color
				crash_time--;
			}
			ps_drive.update(step);
			return;
		}

		if (pause) {
			if (ctx.key_press('p'))
				pause = false;
			return;
		}


		if (scene.collide(target)) hp -= 1;

		// death check
		if (hp < 0) {
			mode = mode_type::crash;
			crash_time = 80;
			return;
		}


		cam.roll(0);
		cam.yaw(0);
		cam.fov(60);
		speed = 10;
		score += score_speed * score_speed_amplifier * step * speed * 10;
		score_speed *= 1 - step;

		bool other_particle = false;
		bool xmove = false;
		bool ymove = false;
		glm::vec3 forward_color{0.5, 0.3, 0.1};
		// key detection
		if (ctx.key('a') || ctx.key('j')) {
			cam.slip(-16*step);
			cam.roll(-20);
			ps_drive.add(
					50,					// life
					target,				// pos
					{ -0.05, 0, 0.01 },	// velocity
					{ 40, 0, 0 },		// acceleration
					{ 0.1, 0.5, 0.1 });	// color
			other_particle = true;
			xmove = !xmove;
		}
		if (ctx.key('d') || ctx.key('l')) {
			cam.slip(+16*step);
			cam.roll(+20);
			ps_drive.add(
					50,					// life
					target,				// pos
					{ +0.05, 0, 0.01 },	// velocity
					{ -40, 0, 0 },		// acceleration
					{ 0.1, 0.5, 0.1 });	// color
			other_particle = true;
			xmove = !xmove;
		}
		if (ctx.key('w') || ctx.key('i')) {
			cam.raise(+12*step);
			cam.yaw(+10);
			ps_drive.add(
					50,					// life
					target,				// pos
					{ 0, 0.05, 0.01 },	// velocity
					{ 0, -40, 0 },		// acceleration
					{ 0.3, 0.1, 0.5 });	// color
			other_particle = true;
			ymove = !ymove;
		}
		if (ctx.key('s') || ctx.key('k')) {
			cam.raise(-12*step);
			cam.yaw(-10);
			ps_drive.add(
					50,					// life
					target,				// pos
					{ 0, -0.05, 0.01 },	// velocity
					{ 0, 40, 0 },		// acceleration
					{ 0.3, 0.1, 0.5 });	// color
			other_particle = true;
			ymove = !ymove;
		}
		if (ctx.key_press('f')) {
			if (mode == mode_type::speed) mode = mode_type::normal;
			else mode = mode_type::speed;
		}
		if (ctx.key_press('r')) {
			if (mode == mode_type::rescue) mode = mode_type::normal;
			else if (ep > 0) mode = mode_type::rescue;
		}
		if (ctx.key_press('p'))
			pause = true;
		if (ctx.key('q'))
			hp = -1;

		switch (mode) {
			case mode_type::speed:
				speed = 20;
				cam.fov(100);
				forward_color = {0.1, 0.5, 0.3};
				ep += 10*step;
				if (ep > 100) {
					hp += (ep-100) / 20;
					ep = 100;
					if (hp > 100) hp = 100;
				}
				break;

			case mode_type::rescue:
				if (ep > 0) {
					float scale;
					if (ep < 20) {
						cam.fov(ep*2+60);
						forward_color = {1, 1, 1};
						scale = 10;
					}
					else {
						cam.fov(100);
						forward_color = {0.5, 0.1, 0.1};
						scale = 20;
					}
					speed = 1;
					hp += scale/2*step;
					ep -= scale*step;
					if (hp > 100) hp = 100;
				}
				else {
					ep = 0;
					mode = mode_type::normal;
				}
				break;

			default: break;
		}

		cam.forward(speed*step);
		cam.update(step);

		if (other_particle) score_speed = (score_speed + step) * (1 + step);
		score_speed_amplifier = 1 + xmove + ymove;

		for (int i=0, n=3-int(other_particle); i<n; i++)
			ps_drive.add(
					100,				// life
					target,				// pos
					{ 0, 0, -0.2 },		// velocity
					{ 0, 0, 40 },		// acceleration
					forward_color);	// color
		ps_drive.update(step);	// must update after all add
	}

	void game::reset()
	{
		cam = gl::camera{};
		scene = gl::scene{};
		pause = false;
		hp = 100;
		ep = 0;
		speed = 10;
		score = 0;
		score_speed = 1;
		score_speed_amplifier = 1;
		mode = mode_type::start;
	}
}

